<?php

/**
 * TODO: Docs
 */
class BIT_Model extends CI_Model
{
	/**
	 * Class name
	 * @var string
	 */
	public static $class_name = __CLASS__;
	
	/**
	 * Various fields
	 * @var array
	 */
	public $fields;
	
	/**
	 * Loaded
	 * @var array
	 */
	static private $loaded = array();
	
	/**
	 * Placeholder classes
	 * @var array
	 */
	static private $placeholders = array();
	
	/**
	 * Class constructor
	 * @return void
	 */
	public function __construct()
	{
		parent::__construct();
	}
	
	/**
	 * Magic setter
	 * @param	string	$key
	 * @param	mixed	$value
	 * @return void
	 */
	public function __set($key, $value)
	{
		if (is_callable(array($this, 'set_' . $key))) 
		{
			call_user_func(array($this, 'set_' . $key), $value);
		}
		else
		{
			$this->$key = $value;
		}
	}
	
	/**
	 * Magic getter
	 * @param	string	$key
	 * @param	mixed	$value
	 * @return void
	 */
	public function __get($key)
	{
		return (is_callable(array($this, 'get_' . $key))) 
		        ? call_user_func(array($this, 'get_' . $key)) 
		        : (isset($this->$key)) 
		          ? $this->$key 
		          : parent::__get($key);
	}
	
	/**
	 * Generic delete
	 * 
	 * @return void
	 */
	public function delete()
	{
		$table = strtolower(get_class($this));
		$fields = $this->getFields();		
		$this->db->where('id', element('id', $fields))->delete($table);
	}
	
	/**
	 * Generic save function
	 * 
	 * @return BIT_Model
	 */
	public function save()
	{
		$table = strtolower(get_class($this));		
		$fields = $this->getFields();
		if (element('id', $fields))
		{
			$this->db->where('id', element('id', $fields))->update($table, $fields);
		}
		else
		{
			$this->db->insert($table, $fields);
			$this->id = $this->db->insert_id();
		}
		
		return $this;
	}
	
	
	/**
	 * Get database fields for model
	 * 
	 * @return array
	 */
	protected function getFields()
	{	
		$fields = array();
		$vars = get_class_vars(get_class($this));
		$parent_vars = get_class_vars(get_class());
		foreach (array_diff(array_keys($vars), array_keys($parent_vars)) as $var)
		{
			$fields[$var] = $this->$var;
		}
		return $fields;
	}
	
	/**
	 * Load data into model
	 * 
	 * @param	BIT_Model	$model
	 * @param	array	$data
	 * @return void
	 */
	static public function load(BIT_Model $model, Array $data)
	{
		$fields = array_keys($model->getFields());
		
		foreach ($fields as $field)
		{
			if (isset($data[$field]))
			{
				$model->$field = $data[$field];
			}
		}
		
		foreach ($data as $key => $value)
		{
			if (strstr($key, '__'))
			{
				list($rel, $relf) = explode('__', $key);
				if (!isset($model->$rel))
				{
					$classname = ucwords($rel);
					$model->$rel = new $classname();
					$model->$rel->$relf = $value;					
				}
				else
				{
					$model->$rel->$relf = $value;
				}
			}
		}
	}
	
	/**
	 * Find all private function
	 * 
	 * @return	array of Category
	 */
	public function _findAll()
	{
		$table = strtolower(get_class($this));		
		$fields = array_keys($this->getFields());
		if($table == "account")
			$table = "accounts";
		
		if (in_array('name', $fields))
		{
			get_instance()->db->order_by('name');
		}
		$r = get_instance()->db->get($table)->result_array();
		if ($r)
		{
			foreach ($r as $row)
			{
				$class = get_class($this);
				$m = new $class;
				static::load($m, $row);
				$all[] = $m;
			}
		}				
		return (isset($all)) ? $all : array();
	}
	
	/**
	 * Find all wrapper
	 * 
	 * @return array of BIT_Model
	 */
	static public function findAll()
	{
		$class = new static::$class_name;
		return $class->_findAll();
	}
	
	/**
	 * Find by id wrapper
	 * 
	 * @return BIT_Model or false on failure
	 */
	static public function findById($id)
	{
		$class = new static::$class_name;
		return $class->_findById($id);		
	}
	
	/**
	 * Find by id
	 * 
	 * @return BIT_Model or false on failure
	 */
	public function _findById($id)
	{
		$table = strtolower(get_class($this));			
		
		$r = $this->db->where('id', $id)->get($table)->result_array();		
		
		if ($r)
		{
			$item = new static::$class_name;
			$this->load($item, current($r));
		}
		
		return (isset($item)) ? $item : false;
	}
	
	/**
	 * Find by field wrapper
	 * 
	 * @return BIT_Model or false on failure
	 */
	static public function findByField(Array $params)
	{
		$class = new static::$class_name;
		return $class->_findByField($params);		
	}
	
	/**
	 * Find by field
	 * 
	 * @return array of BIT_Model
	 */
	public function _findByField(Array $params)
	{
		$items = array();
		$table = strtolower(get_class($this));			
		
		foreach ($params as $field => $value)
		{
			$this->db->where($field, $value);
		}
		$r = $this->db->get($table)->result_array();		
		
		if ($r)
		{
			foreach ($r as $row)
			{
				$item = new static::$class_name;
				$this->load($item, $row);
				$items[] = $item;
			}
		}
		
		return $items;
	}
	
	
	/**
	 * Find or create by name
	 * 
	 * @return BIT_Model
	 */
	static public function findIdCreateByName($name)
	{
		$class = new static::$class_name;
		return $class->_findIdCreateByName($name);
	}
	
	
	/**
	 * Find or create by name wrapper
	 * EARLY RETURN
	 * 
	 * @return BIT_Model
	 */
	public function _findIdCreateByName($name = NULL)
	{
		if (!strlen($name)) return NULL;
		
		$table = strtolower(get_class($this));		
		
		$r = $this->db->where("name LIKE '{$this->db->escape_like_str($name)}'")->get($table)->result_array();
		
		if (!$r)
		{
			$this->db->insert($table, array('name' => $name));
			$id = $this->db->insert_id();
		}
		else
		{
			$row = current($r);
			$id = $row['id'];
		}
		
		return $id;
	}
	
	/**
	 * Load one item by id
	 * @param  int   $id
	 * @return BIT_Model
	 */
	static public function find($id)
	{
		$class = new static::$class_name;
		return $class->_find($id);
	}
	
	/**
	 * Load one item by id
	 * @param  int   $id
	 * @return BIT_Model
	 */
	public function _find($id)
	{
		$table = strtolower(get_class($this));			
		
		$r = $this->db->where('id', $id)->get($table)->row_array();
		
		if ($r)
		{
			$item = new static::$class_name;
			$this->load($item, $r);
		}
		
		return (isset($item)) ? $item : false;
	}
	
	/**
	 * Convert to json
	 * 
	 * @return	string
	 */
	public function json()
	{
		$fields = $this->getFields();
		$vars = get_object_vars($this);
		foreach ($vars as $key => $value)
		{
			if ($value instanceof BIT_Model)
			{
				$fields[$key] = $value->json();
			}
		}
		return json_encode($fields);
	}
	
	/**
	 * Convert to array
	 * 
	 * @return array
	 */
	public function arr()
	{
		$fields = $this->getFields();
		$vars = get_object_vars($this);
		foreach ($vars as $key => $value)
		{
			if ($value instanceof BIT_Model)
			{
				$fields[$key] = $value->arr();
			}
		}
		return $fields;
	}
}
